#include <device/cmos.h>
#include <yonex/types.h>
#include <sys/io.h>
#include <yonex/debug.h>
#include <time.h>
#include <sys/interrupt.h>
#include <assert.h>

#define LOGK(fmt, args...)  DEBUGK(fmt, ##args)

#define CMOS_SECOND     0x00
#define CMOS_MINUTE     0x02
#define CMOS_HOUR       0x04
#define CMOS_WEEKDAY    0x06
#define CMOS_MDAY       0x07
#define CMOS_MONTH      0x08
#define CMOS_YEAR       0x09
#define CMOS_CENTURY    0x32

time_t startup_time;    // 从1970年开始到现在的秒数

void read_time(tm *tm)
{
    do {
        int century = Bcd2Bin(read_cmos(CMOS_CENTURY));
        tm->tm_sec = Bcd2Bin(read_cmos(CMOS_SECOND));
        tm->tm_min = Bcd2Bin(read_cmos(CMOS_MINUTE));
        tm->tm_hour = Bcd2Bin(read_cmos(CMOS_HOUR));
        tm->tm_mday = Bcd2Bin(read_cmos(CMOS_MDAY));
        tm->tm_mon = Bcd2Bin(read_cmos(CMOS_MONTH)) - 1;
        tm->tm_year = Bcd2Bin(read_cmos(CMOS_YEAR)) | (century-19) * 100;
        tm->tm_wday = Bcd2Bin(read_cmos(CMOS_WEEKDAY));
    } while (tm->tm_sec != Bcd2Bin(read_cmos(CMOS_SECOND)));
    tm->tm_isdst = -1;  // 不考虑夏令时
}

void time_init()
{
    tm tm;
    read_time(&tm);
    startup_time = mktime(&tm);
    LOGK("starttp time: %d-%02d-%02d %02d:%02d:%02d\n",
        tm.tm_year + 1900,
        tm.tm_mon + 1,
        tm.tm_mday,
        tm.tm_hour,
        tm.tm_min,
        tm.tm_sec
    );
}

void rtc_handler(int vector)
{
    assert(vector == IRQ_RTC);
    send_eoi(vector);
    read_cmos(CMOS_STATUS_C);

    LOGK("[%d] rtc interrupt ...\n", vector);
}

// 设定一天以内的闹钟 86400秒内
void set_alarm(u32 seconds)
{
    assert(seconds <= 86400);
    u8 secs = seconds % 60;
    seconds /= 60;
    u8 mins = seconds % 60;
    seconds /= 60;
    u32 hours = seconds;
    u8 sec, min, hour;
    do {
        sec = Bcd2Bin(read_cmos(CMOS_SECOND));
        min = Bcd2Bin(read_cmos(CMOS_MINUTE));
        hour = Bcd2Bin(read_cmos(CMOS_HOUR));
    } while(sec != Bcd2Bin(read_cmos(CMOS_SECOND)));
    sec += secs;
    if (sec >= 60) {
        sec -= 60;
        min++;
    }
    min += mins;
    if (min >= 60) {
        min -= 60;
        hour++;
    }
    hour += hours;
    if (hour >= 24)
        hour -= 24;
    write_cmos(ALARM_SECOND, Bin2Bcd(sec));
    write_cmos(ALARM_MINUTE, Bin2Bcd(min));
    write_cmos(ALARM_HOUR, Bin2Bcd(hour));

    write_cmos(CMOS_STATUS_B, 0b00100010);
    read_cmos(CMOS_STATUS_C);   // 允许产生中断
}

void rtc_init()
{
    set_interrupt_handler(IRQ_RTC, rtc_handler);
    set_interrupt_mask(IRQ_RTC, true);
    set_interrupt_mask(IRQ_CASCADE, true);
}
